package belf.migrate.api.table.dbtype;

import belf.migrate.core.exception.ParseMappingFileError;
import belf.migrate.core.util.ConfigUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

/**
 * 初始化时加载各种数据库的所有数据类型。参考数据类型定义文件conf/db-types/DAMENG.xml。<br/>
 * 每种数据库的数据类型按数值类型、字符串类型、时间类型等大类分开，方便管理和使用。
 */
public class DBTypesHolder {
    private static final Logger log = LoggerFactory.getLogger(DBTypesHolder.class);

    private final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();

    private static final DBTypesHolder instance = new DBTypesHolder();

    private final Map<String, DBTypes> mappings = new HashMap<>();

    private DBTypesHolder() {
        File path = new File(ConfigUtil.getDBTypesDir());
        Arrays.stream(
                        path.list())
                .filter(name-> name.endsWith(".xml"))
                .forEach(s -> {
                    try {
                        parseMapping(path.getAbsolutePath() + File.separator + s);
                    } catch (ParseMappingFileError e) {
                        e.printStackTrace();
                        log.error("Parse DB Type error: {}", s);
                    }

                });
    }

    private void parseMapping(String filename) throws ParseMappingFileError {
        log.info("Parse DB Type: {}", filename);
        try {
            DocumentBuilder domBuilder = documentBuilderFactory.newDocumentBuilder();
            Document doc = domBuilder.parse(new InputSource(new InputStreamReader(new FileInputStream(filename))));
            Element root = doc.getDocumentElement();
            String catalog = root.getAttribute("catalog");
            List<String> groups = new ArrayList<>();
            Map<String, List<DataType>> types = new HashMap<>();
            NodeList nodes = root.getElementsByTagName("group");
            for (int i = 0; i < nodes.getLength(); i++) {
                Element groupElement = (Element)nodes.item(i);
                String category =groupElement.getAttribute("category");
                groups.add(category);

                NodeList children = groupElement.getElementsByTagName("type");
                List<DataType> dataTypes = new ArrayList<>();
                for (int j = 0; j < children.getLength(); j++) {
                    Element typeElement = (Element) children.item(j);
                    dataTypes.add(new DataType(typeElement.getAttribute("name"), typeElement.getAttribute("desc")));
                }
                types.put(category, dataTypes);
            }

            DBTypes dbTypes = new DBTypes(catalog, groups, types);
            mappings.put(catalog, dbTypes);
        } catch (ParserConfigurationException | SAXException | IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static DBTypesHolder getInstance() {
        return instance;
    }

    public DBTypes getDBTypes(String catalog) {
        return mappings.get(catalog);
    }
}
